package com.yesun.dcm.config.spring.handler;

import com.yesun.dcm.config.spring.ProtocolConfig;
import com.yesun.dcm.config.spring.RegistryConfig;
import com.yesun.dcm.config.spring.ServiceConfigBean;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.BeanDefinitionHolder;
import org.springframework.beans.factory.config.RuntimeBeanReference;
import org.springframework.beans.factory.config.TypedStringValue;
import org.springframework.beans.factory.support.RootBeanDefinition;
import org.springframework.beans.factory.xml.BeanDefinitionParser;
import org.springframework.beans.factory.xml.ParserContext;
import org.w3c.dom.Element;

import java.lang.reflect.Method;
import java.lang.reflect.Modifier;

/**
 * @author chunmingdeng 2017/9/27 Create.
 * @version 1.0
 * @copyright (c) YESUN. All Rights Reserved.
 * @description: 自定义解析器，用于解析自定义元素节点
 */
public class DcmBeanDefinitionParser implements BeanDefinitionParser {

    private final Class<?> beanClass;
    private final boolean required;

    public DcmBeanDefinitionParser(Class<?> beanClass, boolean required) {
        this.beanClass = beanClass;
        this.required = required;
    }


    /**
     * 解析自定义元素节点
     * @param element
     * @param parserContext
     * @return
     */
    @Override
    public BeanDefinition parse(Element element, ParserContext parserContext) {

        try {
            return parse(element, parserContext, beanClass, required);
        } catch (ClassNotFoundException ex) {
            ex.printStackTrace();
        }
        return null;
    }


    /**
     * 解析XML
     * @param element
     * @param parserContext
     * @param beanClass
     * @param required
     * @return
     * @throws ClassNotFoundException
     */
    private BeanDefinition parse(Element element, ParserContext parserContext, Class<?> beanClass, boolean required) throws ClassNotFoundException {

        RootBeanDefinition bd = new RootBeanDefinition();
        bd.setBeanClass(beanClass);
        bd.setLazyInit(false);

        //读取id属性
        String id = element.getAttribute("id");

        if(StringUtils.isEmpty(id) && required == true) {
            //读取name属性
            String generatedBeanName = element.getAttribute("name");
            if (StringUtils.isEmpty(generatedBeanName)) {
                //读取class属性
                generatedBeanName = element.getAttribute("class");
            }
            if (StringUtils.isEmpty(generatedBeanName)) {
                //读取Class Name
                generatedBeanName = beanClass.getName();
            }
            //给id赋值
            id = generatedBeanName;
            int counter = 2;
            while (parserContext.getRegistry().containsBeanDefinition(id)) {
                //如果bean已经存在，则id改名
                id = generatedBeanName + (counter++);
            }
        }

        //如果id不为空
        if (id != null) {
            //如果指定了id，则不允许重复
            if (parserContext.getRegistry().containsBeanDefinition(id)) {
                throw new IllegalStateException("Duplicate spring bean config, id = " + id);
            }
            //注册bean
            parserContext.getRegistry().registerBeanDefinition(id, bd);
        }
        bd.getPropertyValues().addPropertyValue("id", id);//设置bean的id

        //不同的class，不同的处理
        if (ProtocolConfig.class.equals(beanClass)) {
            DcmNamespaceHandler.protocolDefineNames.add(id);
        } else if (RegistryConfig.class.equals(beanClass)) {
            DcmNamespaceHandler.registryDefineNames.add(id);
        } else if (ServiceConfigBean.class.equals(beanClass)) {
            DcmNamespaceHandler.serviceDefineName.add(id);
            //service时，设置service的ref
            String className = element.getAttribute("class");
            if (StringUtils.isNotEmpty(className)) {
                RootBeanDefinition classDefinition = new RootBeanDefinition();
                //配置相同的ClassLoader
                classDefinition.setBeanClass(Class.forName(className, true, Thread.currentThread().getContextClassLoader()));
                classDefinition.setLazyInit(false);
                //指定Impl结尾会不会有问题？
                bd.getPropertyValues().addPropertyValue("ref", new BeanDefinitionHolder(classDefinition, id + "Impl"));
            }
        }
        //解析set方法，将xml配置赋值到bean
        for (Method setter : beanClass.getMethods()) {
            String name = setter.getName();
            if (name.length() <= 3 || !name.startsWith("set") || !Modifier.isPublic(setter.getModifiers())) {
                continue;
            }
            String property = name.substring(3, 4).toLowerCase() + name.substring(4);
            if ("id".equals(property)) {
                bd.getPropertyValues().addPropertyValue("id", id);
                continue;
            }
            String value = element.getAttribute(property);
            if (StringUtils.isEmpty(value)) {
                continue;
            }
            value = value.trim();

            Object reference;
            if ("ref".equals(property)) {
                reference = new RuntimeBeanReference(value);
            } else {
                reference = new TypedStringValue(value);
            }
            bd.getPropertyValues().addPropertyValue(property, reference);
        }
        return bd;
    }


}
